﻿using System;
using System.Collections.Generic;

namespace Sustainalytics.ESGRatings.Primitives
{
    public class RatingsResult : IDisposable
    {
        BufferManager manager;

        public RatingsResult(BufferManager manager, int companiesCount, int indicatorsCount, int clustersCount, int clusterGroupsCount, bool computeIndicatorRatings, bool includeAggregatedWeights, bool includeWeights, bool isDefaultWeightMatrix = false)
        {
            this.manager = manager;
            this.CompaniesCount = companiesCount;
            this.ClustersCount = clustersCount;
            this.IndicatorsCount = indicatorsCount;
            this.ClusterGroupsCount = clusterGroupsCount;

            this.TimedSteps = new List<TimedStep>();


            this.ComputeIndicatorsRatings = computeIndicatorRatings;
            this.ComputeAggregatedWeights = includeAggregatedWeights;
            this.IncludeWeights = includeWeights;
            this.IsDefaultWeightMatrix = isDefaultWeightMatrix;
        }

        public bool ComputeIndicatorsRatings { get; protected set; }
        public bool ComputeAggregatedWeights { get; protected set; }

        public bool IncludeWeights { get; protected set; }

        public bool IsDefaultWeightMatrix { get; protected set; }

        public void AllocateBuffers()
        {
            this.Ratings = manager.Get<float>(CompaniesCount * ClustersCount);
            this.CompanyIndices = manager.Get<int>(CompaniesCount * ClustersCount);
            this.IsInReferenceUniverse = manager.Get<bool>(CompaniesCount);

            if (this.ComputeIndicatorsRatings)
                this.IndicatorsRatings = manager.Get<float>(CompaniesCount * IndicatorsCount);

            if (this.ComputeAggregatedWeights)
                this.AggregatedWeights = manager.Get<float>(CompaniesCount * ClustersCount);

            if (this.IncludeWeights)
                this.Weights = manager.Get<float>(CompaniesCount * IndicatorsCount);

            this.Rankings = manager.Get<int>(CompaniesCount * ClustersCount);
            this.Percentile = manager.Get<float>(CompaniesCount * ClustersCount);
            this.Group_Counts = manager.Get<int>(CompaniesCount * ClustersCount);

            this.Group_Avgs = manager.Get<float>(ClusterGroupsCount * ClustersCount);
            this.Group_Mins = manager.Get<float>(ClusterGroupsCount * ClustersCount);
            this.Group_Maxs = manager.Get<float>(ClusterGroupsCount * ClustersCount);


        }


        public int CompaniesCount;
        public int ClustersCount;
        public int IndicatorsCount;

        public float[] Ratings;
        public float[] IndicatorsRatings;
        public float[] AggregatedWeights;
        public float[] Weights;
        public int[] CompanyIndices;
        public bool[] IsInReferenceUniverse;
        public float[] Percentile;

        public int[] Rankings;
        public int ClusterGroupsCount;
        public float[] Group_Mins;
        public float[] Group_Maxs;
        public float[] Group_Avgs;
        public int[] Group_Counts;

        private bool _disposed = false;
        public void Dispose()
        {
            if (_disposed)
                return;

            _disposed = true;

            if (Ratings != null)
                manager.Release(Ratings);

            if (CompanyIndices != null)
                manager.Release(CompanyIndices);

            if (IndicatorsRatings != null)
                manager.Release(IndicatorsRatings);

            if (IsInReferenceUniverse != null)
                manager.Release(IsInReferenceUniverse);

            Ratings = null;
            CompanyIndices = null;
            IsInReferenceUniverse = null;
            IndicatorsRatings = null;
            AggregatedWeights = null;
            Weights = null;
            Percentile = null;
        }


        public List<TimedStep> TimedSteps { get; protected set; }

    }

    public class ReusableArray<T> : IDisposable
    {
        BufferManager manager;
        int count = 0;

        public ReusableArray(BufferManager manager, int count)
        {
            this.manager = manager;
            this.count = count;

            Data = manager.Get<T>(count);
        }

        public T[] Data;

        private bool _disposed = false;
        public void Dispose()
        {
            if (_disposed)
                return;

            _disposed = true;
            manager.Release(Data);
            Data = null;
        }
    }


    public class TimedStep
    {

        public TimedStep(string opName, double milliseconds)
        {
            this.Name = opName;
            this.Milliseconds = milliseconds;
        }

        public string Name { get; protected set; }
        public double Milliseconds { get; protected set; }
    }


}
